package com.example.cloud.config;

import java.lang.reflect.Field;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ReflectionUtils;

import com.google.common.collect.Lists;
import com.netflix.discovery.EurekaClient;

import io.grpc.Channel;
import io.grpc.ClientInterceptor;

/**
 * 注入GrpcClient
 * 
 * @author tongyufu
 *
 */
public class GrpcClientBeanPostProcessor implements BeanPostProcessor {

    private Logger log = LoggerFactory.getLogger(getClass());
    @Autowired
    private EurekaClient eurekaClient;
    @Autowired
    private GrpcChannelFactory channelFactory;
    @Autowired
    private DefaultListableBeanFactory beanFactory;

    @Override
    public Object postProcessBeforeInitialization(Object bean,
                                                  String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean,
                                                 String beanName) throws BeansException {

        Class<? extends Object> clazz = bean.getClass();
        do {
            for (Field field : clazz.getDeclaredFields()) {
                if (!field.isAnnotationPresent(GrpcClient.class)) {
                    continue;
                }
                GrpcClient annotation = AnnotationUtils.getAnnotation(field, GrpcClient.class);
                /*InstanceInfo instanceInfo = eurekaClient.getNextServerFromEureka(annotation.value(),
                    false);
                ManagedChannel channel = ManagedChannelBuilder
                    .forAddress(instanceInfo.getIPAddr(), instanceInfo.getPort()).usePlaintext(true)
                    .build(); */
                List<ClientInterceptor> list = Lists.newArrayList();
                for (Class<? extends ClientInterceptor> clientInterceptorClass : annotation
                    .interceptors()) {
                    ClientInterceptor clientInterceptor;
                    if (beanFactory.getBeanNamesForType(ClientInterceptor.class).length > 0) {
                        clientInterceptor = beanFactory.getBean(clientInterceptorClass);
                    } else {
                        try {
                            clientInterceptor = clientInterceptorClass.newInstance();
                        } catch (Exception e) {
                            throw new BeanCreationException("Failed to create interceptor instance",
                                e);
                        }
                    }
                    list.add(clientInterceptor);
                }
                Channel channel = channelFactory.createChannel(annotation.value(), list);
                ReflectionUtils.makeAccessible(field);
                try {
                    ReflectionUtils.setField(field, getTargetBean(bean), channel);
                } catch (Exception e) {
                    throw new BeanInitializationException("初始化bean失败", e);
                }
                log.debug("注入grpc client:" + field.toString());
            }
            clazz = clazz.getSuperclass();
        } while (clazz == null);
        return bean;
    }

    private Object getTargetBean(Object bean) throws Exception {
        Object target = bean;
        while (AopUtils.isAopProxy(target)) {
            target = ((Advised) target).getTargetSource().getTarget();
        }
        return target;
    }
}
